#pragma once
#include <string>
#include <crane/defs.hpp>
#include <crane/core/error.hpp>

CRANE_NS_BEGIN
namespace net {

struct Url {
    std::string         m_url;
    std::string_view    m_scheme;
    std::string_view    m_authority;
    std::string_view    m_user;
    std::string_view    m_pass;
    std::string_view    m_host;
    std::string_view    m_port;
    std::string_view    m_path;
    std::string_view    m_query;
    std::string_view    m_fragment;

    Url() = default;
    Url(Url && other) = default;
    Url(const Url &other);

    Url& operator=(const Url &other);
    Url& operator=(Url && other) = default;
};

void URL_parse(Url & url, std::string_view str);



/**
 * @brief URL编码,输入字符串按application/x-www-form-urlencoded编码格式编码输出.
 */
struct URLEncoder {
    CRANE_NONINSTANCIABLE(URLEncoder);

    static size_t encode(const char* src, size_t src_len, char* dst, size_t dst_len);
    static std::string encode(const std::string &in);
};





static const int g_url_char_should_encode[256] = {
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0-15
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 16-31
        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, // 32-47 (' ', '*', '-', '.') , 按照application/x-www-form-urlencoded与Java URLEncoder规则，空格转换成+，而不是%20
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, // 48-63 (0-9)
        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 64-79 (A-O)
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, // 80-95 (P-Z, '_')
        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 96-111 (`a-o`)
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, // 112-127 (`p-z`)
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128-143
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144-159
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160-175
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176-191
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192-207
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208-223
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224-239
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1  // 240-255
};

static const char g_url_char_hex[16] = {
    '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G'
};


CRANE_INLINE
size_t URLEncoder::encode(const char *in, size_t inlen, char * out, size_t outlen)
{
    const char * p_in = in;
    const char * p_in_end = in + inlen;
    char * p_out = out;
    char * p_out_end = out + outlen;
    
    while ( p_in != p_in_end && p_out != p_out_end ) {
        char ch = *p_in;
        
        if ( g_url_char_should_encode[ch]  == 0 ) {
            if ( ch == ' ' ) *p_out = '+';
            else  *p_out = ch;
            p_out++;
            p_in++;
            continue;
        }

        p_out += 3; // 预留空间
        if ( p_out >= p_out_end ) {
            break;
        }

        *(p_out - 3) = '%';
        *(p_out - 2) = g_url_char_hex[ch >> 4];
        *(p_out - 1) = g_url_char_hex[ch & 0x0F];

        ++p_in;
    } // while

    return p_out - out;
}

CRANE_INLINE
std::string URLEncoder::encode(const std::string &in) {
    std::string out;
    out.resize(in.size() * 3 + 1);  // 就算全部转码，3被内存都够了

    size_t outlen = 0;
    do {        // 保险起见再套个循环
        outlen = encode(in.c_str(), in.size(), &out[0], out.size());
        if ( outlen < out.size() ) out.resize(outlen);
    } while ( outlen >= out.size());
    return out;
}

} // net
CRANE_NS_END


#include "impl/url_impl.inl"
